Skip to content

HBASE-30323 [RSGroup] Forward-port HBASE-22658 to branch-2 - #8547

Open
Umeshkumar9414 wants to merge 4 commits into
apache:branch-2from
Umeshkumar9414:HBASE-30323
Open

HBASE-30323 [RSGroup] Forward-port HBASE-22658 to branch-2#8547
Umeshkumar9414 wants to merge 4 commits into
apache:branch-2from
Umeshkumar9414:HBASE-30323

Conversation

@Umeshkumar9414

@Umeshkumar9414 Umeshkumar9414 commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

RegionMover.unloadRegions() previously picked destination servers from all online RegionServers regardless of RSGroup membership, potentially trying to move regions out of their assigned group during server decommission, althoug HMaster prevents it.

This is a branch-2-compatible port of HBASE-22740. On master/branch-3 the fix used admin.getRSGroup() which is integrated directly into the Admin interface (via HBASE-22971). On branch-2 RSGroup remains an optional coprocessor module (hbase-rsgroup), so we:

  1. Check whether RSGroups are enabled via RSGroupTableAccessor.isRSGroupsEnabled(conn) (hbase-client) to skip the RSGroup path on clusters that do not use RSGroups.
  2. Look up the server's RSGroupInfo by reading the hbase:rsgroup system table directly via RSGroupTableAccessor.getAllRSGroupInfo(conn), this avoids a circular dependency between hbase-server and hbase-rsgroup.
  3. Filter the destination server list to only servers in the same RSGroup (filterRSGroupServers); filtering is always applied by membership — the DEFAULT_GROUP short-circuit that could leak regions across groups is removed.
  4. If RSGroups are enabled but the server cannot be matched to any group, throw IOException rather than falling back to treating every online server as a valid destination — that fallback would defeat RSGroup isolation. This can happen from hostname-vs-IP address-form mismatches (HBASE-27304).

Tests:

  • TestRegionMoverWithRSGroupEnable (hbase-rsgroup): 5-node mini cluster integration test with RSGroupAdminEndpoint enabled. Verifies that unloading a non-default-group server places all regions exclusively on the remaining server in that group (positive assertion) and that no default-group server receives any of those regions (isolation assertion). The decommission target is chosen by checking which server actually hosts a region, since moveTableRegionsToGroup() places regions via randomAssignment(). Also tests the guard path: unloading a default-group server succeeds end-to-end when RSGroups are enabled, with destinations filtered to the default group.
  • TestRegionMoverFilterRSGroupServers (hbase-server): unit tests for filterRSGroupServers() — default group returns full server list, non-default group filters to members only, no-match group returns empty.

@sanjeet006py sanjeet006py left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about adding an IT test case for scenario when all servers in test RS group go down and fallback config is enabled (hbase.rsgroup.fallback.enable = true), then regions from test RS group do move to default RS group servers on calling RegionMover.unload().

.map(JVMClusterUtil.RegionServerThread::getRegionServer)
.filter(rs -> rs.getServerName().equals(defaultSN)).findFirst().get();
assertEquals(0, decommRS.getRegions(defaultTable).size(),
"Decommissioned default-group server must hold no regions after unload");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we also please add assertion that default group regions don't land on test RS group servers?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added the assertion in testUnloadDefaultGroupServerWithRSGroupEnabled


/** A non-default group with one member must return only that member. */
@Test
public void testNonDefaultGroupFiltersToMembers() throws Exception {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add a test case where there is default RS group and some test RS group. And, assertion is that output of filter for default RS group should not have any server of test RS group. Please correct me if my understanding is wrong.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added in testNonDefaultGroupFiltersToMembers

@Umeshkumar9414

Copy link
Copy Markdown
Contributor Author

How about adding an IT test case for scenario when all servers in test RS group go down and fallback config is enabled (hbase.rsgroup.fallback.enable = true), then regions from test RS group do move to default RS group servers on calling RegionMover.unload().

There is already test written for that, 'TestRSGroupsFallback'.

@virajjasani

Copy link
Copy Markdown
Contributor

The PR is ready to merge. We just need to co-ordinate when to merge this, I think it will be better to merge this after your fix on master branch is first merged and backported to branch-2 and others. Please let me know if there is any concern, I am fine either way.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates RegionMover to respect RSGroup membership when selecting unload destinations on branch-2.

Changes:

  • Queries RSGroup membership through the optional coprocessor RPC.
  • Filters candidate RegionServers to the source server’s group.
  • Relocates protobuf definitions and adds unit/integration tests.

Reviewed changes

Copilot reviewed 3 out of 4 changed files in this pull request and generated 3 comments.

File Description
hbase-server/.../RegionMover.java Adds RSGroup lookup and destination filtering.
hbase-server/.../TestRegionMoverFilterRSGroupServers.java Tests filtering behavior.
hbase-rsgroup/.../TestRegionMoverWithRSGroupEnable.java Adds RSGroup-enabled integration tests.
hbase-protocol/.../RSGroupAdmin.proto Makes RSGroup RPC definitions available through hbase-protocol.
Suppressed comments (1)

hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRegionMoverWithRSGroupEnable.java:184

  • This assertion claims to count this table's nine regions but counts every online region on the server. Any unrelated region left or later assigned there causes a false failure; assert against getRegions(TABLE_NAME) as the isolation checks below already do.
    assertEquals(9, onlineRS.getNumberOfOnlineRegions(),
      "All 9 regions must be on the single remaining server in the test RSGroup");

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +874 to +878
if (!resp.hasRSGroupInfo()) {
LOG.debug("No RSGroup found for {}:{} — server may not be registered or address form "
+ "(hostname vs IP) may not match what the RS registered with", host, port);
return null;
}
Comment on lines +167 to +168
Address decommission = rsservers.get(0);
Address online = rsservers.get(1);
Comment on lines +199 to +200
* Unloading a server that is in the default RSGroup must still succeed end-to-end when RSGroups
* are enabled. The server's regions should be spread across all available servers (not filtered).
@apurtell

Copy link
Copy Markdown
Contributor

@virajjasani Copilot found at least one issue (the first one) that should be resolved before merge.

@apurtell

apurtell commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

The move of RSGroupAdmin.proto from hbase-rsgroup to hbase-protocol is not necessary and the way you've left the state of the POM in hbase-rsgroup the protobuf compilation still happens there but now there's no input files. I guess it works?? but is messy.

We move RSGroups out of the separate module into core in HBase 3 and up, so there's that, but the way we hedged RSGroups in branch-2 puts everything else in hbase-rsgroup. It's fine and even customary to have protos outside of hbase-protos. We have the protos for REST broken out into hbase-rest, for example, so this is maybe a smell.

This is not a dealbreaker, but there is an alternative that does not require it. Consider just using RSGroupTableAccessor from hbase-client. Skip the coprocessor RPC and read the hbase:rsgroup system table directly via the accessor that already exists in hbase-client. Something like:

private RSGroupInfo getRSGroupInfo(String host, int port) throws IOException {
  if (!RSGroupTableAccessor.isRSGroupsEnabled(conn)) {
    return null;
  }
  Address addr = Address.fromParts(host, port);
  for (RSGroupInfo info : RSGroupTableAccessor.getAllRSGroupInfo(conn)) {
    if (info.containsServer(addr)) {
      return info;
    }
  }
  return null;
}

I also like this approach better because the invocation of the coprocessor is brittle. It works today but any subclass or wrapper would break it, if someone is extending rsgroups and implementing their own enhanced coprocessors. Unlikely but not impossible.

Umeshkumar9414 added a commit to Umeshkumar9414/hbase that referenced this pull request Aug 17, 2026
…lookup, deterministic test

Switches RegionMover's RSGroup lookup from a coprocessor RPC
(RSGroupAdminEndpoint) to reading the hbase:rsgroup table directly via
RSGroupTableAccessor, as suggested by apurtell on PR apache#8547. This moves
RSGroupAdmin.proto back to hbase-rsgroup (no longer needed in
hbase-protocol) since hbase-server no longer talks to the coprocessor.

Also addresses three Copilot review findings on the same PR:
- getRSGroupInfo() now throws IOException instead of returning null
  when RSGroups are enabled but the server can't be matched to any
  group, so unloadRegions() doesn't silently fall back to treating
  every online server as a valid destination.
- testUnloadRegionsRespectsRSGroup picks its decommission target by
  checking which rsservers member actually hosts a region, since
  moveTableRegionsToGroup() places regions via randomAssignment and
  rsservers.get(0) is not guaranteed to host anything.
- Corrected the Javadoc on testUnloadDefaultGroupServerWithRSGroupEnabled
  to state that destinations are filtered to the default group, matching
  the test's actual isolation assertion.
@Umeshkumar9414

Copy link
Copy Markdown
Contributor Author

Pushed 3dcff72 addressing the remaining review feedback:

@apurtell's suggestion — switched getRSGroupInfo() to read hbase:rsgroup directly via RSGroupTableAccessor (from hbase-client) instead of the RSGroupAdminEndpoint coprocessor RPC. This let me move RSGroupAdmin.proto back to hbase-rsgroup, so hbase-protocol no longer carries it — the messy proto relocation is gone.

Copilot findings:

  • getRSGroupInfo() now throws IOException instead of returning null when RSGroups are enabled but the server can't be matched to any group. Previously that case fell through to treating every online server as a valid destination, which defeats RSGroup isolation during decommission.
  • testUnloadRegionsRespectsRSGroup now picks the decommission target by checking which rsservers member actually hosts a TABLE_NAME region, rather than assuming rsservers.get(0). moveTableRegionsToGroup() places regions via randomAssignment(), so the old assumption could let the test pass without exercising the move/filter path at all.
  • Fixed the Javadoc on testUnloadDefaultGroupServerWithRSGroupEnabled — it said regions "spread across all available servers (not filtered)" but the test actually asserts destinations are filtered to the default group.

Verified: TestRegionMoverFilterRSGroupServers (3/3) and TestRegionMoverWithRSGroupEnable (2/2) pass, checkstyle and spotbugs clean on both hbase-server and hbase-rsgroup.

@Umeshkumar9414

Copy link
Copy Markdown
Contributor Author

The move of RSGroupAdmin.proto from hbase-rsgroup to hbase-protocol is not necessary and the way you've left the state of the POM in hbase-rsgroup the protobuf compilation still happens there but now there's no input files. I guess it works?? but is messy.

We move RSGroups out of the separate module into core in HBase 3 and up, so there's that, but the way we hedged RSGroups in branch-2 puts everything else in hbase-rsgroup. It's fine and even customary to have protos outside of hbase-protos. We have the protos for REST broken out into hbase-rest, for example, so this is maybe a smell.

This is not a dealbreaker, but there is an alternative that does not require it. Consider just using RSGroupTableAccessor from hbase-client. Skip the coprocessor RPC and read the hbase:rsgroup system table directly via the accessor that already exists in hbase-client. Something like:

private RSGroupInfo getRSGroupInfo(String host, int port) throws IOException {
  if (!RSGroupTableAccessor.isRSGroupsEnabled(conn)) {
    return null;
  }
  Address addr = Address.fromParts(host, port);
  for (RSGroupInfo info : RSGroupTableAccessor.getAllRSGroupInfo(conn)) {
    if (info.containsServer(addr)) {
      return info;
    }
  }
  return null;
}

I also like this approach better because the invocation of the coprocessor is brittle. It works today but any subclass or wrapper would break it, if someone is extending rsgroups and implementing their own enhanced coprocessors. Unlikely but not impossible.

Thanks a lot. @apurtell . I missed this.

Umeshkumar9414 and others added 2 commits August 18, 2026 02:00
RegionMover.unloadRegions() previously picked destination servers from
all online RegionServers regardless of RSGroup membership, potentially
trying to move regions out of their assigned group during server
decommission, althoug HMaster prevents it.

This is a branch-2-compatible port of HBASE-22740. On master/branch-3
the fix used admin.getRSGroup() which is integrated directly into the
Admin interface (via HBASE-22971). On branch-2 RSGroup remains an
optional coprocessor (RSGroupAdminEndpoint), so we:

1. Check for RSGroupAdminEndpoint via getMasterCoprocessorNames() to
   skip the RSGroup path on clusters that do not use RSGroups.
2. Call getRSGroupInfoOfServer() via the RSGroupAdminService coprocessor
   RPC, using the unshaded protobuf service from hbase-protocol.
3. Filter the destination server list to only servers in the same
   RSGroup (filterRSGroupServers); filtering is always applied by
   membership — the DEFAULT_GROUP short-circuit that could leak regions
   across groups is removed.
4. Log a DEBUG message when hasRSGroupInfo()==false to aid diagnosis
   of hostname-vs-IP address-form mismatches (HBASE-27304).

RSGroupAdmin.proto is moved from hbase-rsgroup to hbase-protocol so
that hbase-server can use RSGroupAdminService without creating a
circular dependency with hbase-rsgroup. Both modules depended on the
same generated FQN; consolidating in hbase-protocol (which both
already depend on) is best I could think of.

Tests:
- TestRegionMoverWithRSGroupEnable (hbase-rsgroup): 5-node mini
  cluster integration test with RSGroupAdminEndpoint enabled. Verifies
  that unloading a non-default-group server places all regions
  exclusively on the remaining server in that group (positive assertion)
  and that no default-group server receives any of those regions
  (isolation assertion). Also tests the guard path: unloading a
  default-group server succeeds end-to-end when RSGroups are enabled.
- TestRegionMoverFilterRSGroupServers (hbase-server): unit tests for
  filterRSGroupServers() — default group returns full server list,
  non-default group filters to members only, no-match group returns
  empty.

Co-authored-by: Claude Sonnet 4.6 <claude@anthropic.com>
…lookup, deterministic test

Switches RegionMover's RSGroup lookup from a coprocessor RPC
(RSGroupAdminEndpoint) to reading the hbase:rsgroup table directly via
RSGroupTableAccessor, as suggested by apurtell on PR apache#8547. This moves
RSGroupAdmin.proto back to hbase-rsgroup (no longer needed in
hbase-protocol) since hbase-server no longer talks to the coprocessor.

Also addresses three Copilot review findings on the same PR:
- getRSGroupInfo() now throws IOException instead of returning null
  when RSGroups are enabled but the server can't be matched to any
  group, so unloadRegions() doesn't silently fall back to treating
  every online server as a valid destination.
- testUnloadRegionsRespectsRSGroup picks its decommission target by
  checking which rsservers member actually hosts a region, since
  moveTableRegionsToGroup() places regions via randomAssignment and
  rsservers.get(0) is not guaranteed to host anything.
- Corrected the Javadoc on testUnloadDefaultGroupServerWithRSGroupEnabled
  to state that destinations are filtered to the default group, matching
  the test's actual isolation assertion.
…rage

Port the meta-RS exclusion and pre-unload precondition assertion from
the master PR (apache#8552) version of testUnloadDefaultGroupServerWithRSGroupEnabled
so both branches exercise the same scenario.
…vers

Explains what each failed assertion means, and rewraps the class
Javadoc to stay under the 100-char line limit.
@Umeshkumar9414

Copy link
Copy Markdown
Contributor Author

I checked both are clean cherry-pick to 2.5 and 2.6.

@virajjasani

Copy link
Copy Markdown
Contributor

Triggered github workflow, please keep an eye if we get any non-flaky test failures

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants